package com.OnTimeTeam.OnTime;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import com.google.android.gms.maps.model.LatLng;

import android.content.Context;
import android.content.Intent;

import android.net.Uri;
import android.os.StrictMode;
import android.os.Bundle;
import android.util.Log;
import android.webkit.URLUtil;


/**********************************************************************************
 * ClassName: RegularRoadsQuery
 
 * MoreAbout: This class sends URL query to google-maps
 * asking for route from origin address to dest address.
 * After getting response, create XML and parse it to the features needed.
 
 * Methods: 

	public void setParameters() - set all parameters (distance, duration, cords...)
	private Node getNodeByName(Node fatherNode, String nodeName) - returns Node
		from xml tree.
		
	public String getTotalDistance() - returns distance as string.
	public String getTotalDuration() - returns duration as string
	public int getTotalDurationNumeric() - returns duration as numberic value
	
	public double getSrcLat() - return origin's latitude
	public double getSrcLng() - return origin's longtitude
	public double getDestLat() - return destination's latitude
	public double getDestLng() - return destination's longtitude
 *********************************************************************************/
public class RegularRoadsQuery extends RoadsQuery{

	//the document - through it, we'll traverse the XML tree
	private Document doc;
	
	//Distance and Duration
	private String totalDistance;
	private String totalDuration;
	private int totalDurationNumeric;
	
	//Source longitude and latitude
	private double srcLong;
	private double srcLat;
	
	
	//Destination longitude and latitude
	private double destLong;
	private double destLat;
	
	private ArrayList<LatLng> alRoute;
	private double[] coordsDoubleArray;
	//C'tor - gets origin string and dest string, init' 'Document doc'.
	public RegularRoadsQuery(String originAddress, String destAddress, Boolean useCurrentLocation, String mode){

		//init' parameters
		URL url = null;
		totalDistance = null;
		totalDuration = null;
		totalDurationNumeric = 0;
		
		//Init coordinates longtitude and latitude to -1
		srcLong = srcLat = destLong = destLat = -1;

		//Enter try block
		try {
			
			//String s = "http://maps.googleapis.com/maps/api/directions/xml?origin=\"" + originAddress + "\"&destination=\"" + destAddress + "\"&sensor=" + useCurrentLocation.toString() + "&mode=" + mode;
			 
			//enter query to URL with "sensor" and "mode"
			url = new URL("http://maps.googleapis.com/maps/api/directions/xml?origin=\"" + URLEncoder.encode(originAddress, "UTF-8")  + 
					"\"&destination=\"" + URLEncoder.encode(destAddress, "UTF-8")  + 
					"\"&sensor=" + useCurrentLocation.toString() + 
					"&mode=" + mode);
			URLConnection connection = url.openConnection();
			connection.setConnectTimeout(7000);
			connection.setReadTimeout(7000);
			
			//Enter to BufferedReader the whole xml
			BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			
			//BufferedReader -> String
			String strXml = in.readLine();
			String tmpLine;
			while ((tmpLine = in.readLine()) != null) 
				strXml = strXml + "\n" + tmpLine;
			
			
			
			
            XmlPullParserFactory xppfFactory = XmlPullParserFactory.newInstance();
            xppfFactory.setNamespaceAware(true);
            XmlPullParser xppParses = xppfFactory.newPullParser();

            xppParses.setInput(new StringReader(strXml));
            int iEventType = xppParses.getEventType();
            String szTag = "";
            String szText = "";
            boolean bStep = false;
            double iLat = 0;
            double iLong = 0;
			
            alRoute = new ArrayList<LatLng>();
            		
            while(iEventType != XmlPullParser.END_DOCUMENT) 
            {     
                 iEventType = xppParses.next();

                 if(iEventType == XmlPullParser.START_TAG) 
                 {
                     szTag = xppParses.getName();

                     if(szTag.equals("step"))
                         bStep = true;
                     else if(szTag.equals("overview_polyline"))
                    	 break;
                 }
                 else if(iEventType == XmlPullParser.TEXT)      
                 {  
                     if(szTag.equals("points")){
                    	 if(!xppParses.getText().equals("") && !xppParses.getText().startsWith("\n")) {
                    		 decodePoly(xppParses.getText());
	                    	 Log.e("Polyline", xppParses.getText());
                    	 }
                         szText = "";
                     }
                     else
                         szText = xppParses.getText().trim();
                 }          
                 else if(iEventType == XmlPullParser.END_TAG) 
                 {
                     if(xppParses.getName().equals("step"))
                     {
                         bStep = false;
                     }
                    else if(bStep && xppParses.getName().equals("start_location") || xppParses.getName().equals("end_location"))
                     {
                    	LatLng gpPoint = new LatLng(iLat,iLong);
                        //alRoute.add(gpPoint);
                     }
                     else if(bStep && xppParses.getName().equals("lat"))
                     {
                         iLat = (Double.parseDouble(szText));
                     }
                     else if(bStep && xppParses.getName().equals("lng"))
                     {
                         iLong = (Double.parseDouble(szText));
                     }
                 }
            }
			
			coordsDoubleArray = new double[alRoute.size()*2];
			for(int i = 0, j = 0; i < alRoute.size(); ++i,j+=2)  {
				coordsDoubleArray[j] = alRoute.get(i).latitude;
				coordsDoubleArray[j+1] = alRoute.get(i).longitude;
			}

			/***NOW strXml holds the xml content***/
			
			//using DOM methods, parse the xml
			DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(strXml));
			
	        //now doc holds the data
	        doc = db.parse(is);

		} catch (Exception e) {//If any Exception was caught
			e.printStackTrace();
		}
		
	}//EO-C'tor
	
	
    private List<LatLng> decodePoly(String encoded) {
   	 
        List<LatLng> poly = new ArrayList<LatLng>();
        int index = 0, len = encoded.length();
        int lat = 0, lng = 0;
 
        while (index < len) {
            int b, shift = 0, result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lat += dlat;
 
            shift = 0;
            result = 0;
            do {
                b = encoded.charAt(index++) - 63;
                result |= (b & 0x1f) << shift;
                shift += 5;
            } while (b >= 0x20);
            int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
            lng += dlng;
 
            LatLng p = new LatLng((((double) lat / 1E5)),
                                 (((double) lng / 1E5)));
            alRoute.add(p);
        }
        return poly;
    }
	
	
	//This method travese the XML route tree and get the needed values from nodes
	public void setParameters(){
		
		//Init' parameters
		String totalDuration = null;
		String totalDistance = null;
		
		//Source longitude and latitude
		double srcLongDouble = -1;
		double srcLatDouble = -1;
		
		//Destination longitude and latitude
		double destLongDouble = -1;
		double destLatDouble = -1;

		double totalDurationNumeric = 0;
		
		
		
		//The root element
		if(doc == null)
			return;
		Node rootNode = doc.getDocumentElement();//DirectionsResponse
		
		//Check if found result
		if(null == getNodeByName(rootNode, "status"))//In case no results were found - return null
			return;
		
		//get the node 'route' (son of rootNode)
		Node routeNode = getNodeByName(rootNode, "route");
		
		if(null != routeNode){
			
			//get the node 'leg' (son of routeNode)
			Node legNode = getNodeByName(routeNode, "leg");
			
			//get the node 'duration'
			if(null != legNode){
				
				//get node 'duration' (son of legNode)
				Node durationNode = getNodeByName(legNode, "duration");
				
				if(null != durationNode){
					
					//Get value of the duration node (contains duration of the route)
					Node textNode = getNodeByName(durationNode, "text");
					totalDuration = textNode.getTextContent();//init' the duration
					
					Node durationValueNode = getNodeByName(durationNode, "value");
					totalDurationNumeric = Double.valueOf(durationValueNode.getTextContent());//init' numeric duration
					
				}
				
				//get 'distance' node, son of legNode
				Node distanceNode = getNodeByName(legNode, "distance");
				
				if(null != distanceNode){
					
					//get text node
					Node textNode = getNodeByName(distanceNode, "text");		
					
					//init' the distance to the target
					totalDistance = textNode.getTextContent();
				}
				
				//Get coordinations (lat' and long' of start-location)
				Node startCord = getNodeByName(legNode, "start_location");
				
				if(null != startCord){
					
					Node srcLatNode = getNodeByName(startCord, "lat");
					Node srcLngNode = getNodeByName(startCord, "lng");
					
					//init' the distance to the target
					srcLatDouble = Double.valueOf(srcLatNode.getTextContent());
					srcLongDouble = Double.valueOf(srcLngNode.getTextContent());
				}				
				
				//Get coordinations (lat' and long' of end-location)
				Node destCord = getNodeByName(legNode, "end_location");
				
				if(null != destCord){
					
					Node destLatNode = getNodeByName(destCord, "lat");
					Node destLngNode = getNodeByName(destCord, "lng");
					
					//init' the distance to the target
					destLatDouble = Double.valueOf(destLatNode.getTextContent());
					destLongDouble = Double.valueOf(destLngNode.getTextContent());
				}	
				
			}
			
		}//end of Condition
		
		//duration and distance
		this.totalDuration = totalDuration;
		this.totalDistance = totalDistance;
		
		//set total duration in minutes
		totalDurationNumeric /= 60.0;
		if((totalDurationNumeric - (int)totalDurationNumeric) >= 0.5)
			this.totalDurationNumeric = (int)(totalDurationNumeric+1);
		else
			this.totalDurationNumeric = (int)totalDurationNumeric;
		
		//src cords
		this.srcLat = srcLatDouble;
		this.srcLong = srcLongDouble;
		
		//dst cord
		this.destLat = destLatDouble;
		this.destLong = destLongDouble;
		
	}//end of getTotalDuration
	
	private Node getNodeByName(Node fatherNode, String nodeName){
		
		Node node = null;
		
		//get children of root
		NodeList children = fatherNode.getChildNodes();
		
		//if not null
		if(children != null) {
			
			//run over the list
			for(int i = 0; i < children.getLength(); i++ ) {

				if(children.item(i).getNodeName().equals(nodeName)){
					node = children.item(i);
					break;	
				}
			}
		}
		
		return node;
	}
	
	public static boolean addressVerificationByGeoCoder(String address)
	{
		
		try {  
			 
			String query = "http://maps.googleapis.com/maps/api/geocode/xml?address=" + URLEncoder.encode(address, "UTF-8") + "&sensor=false";
			
			URL url = new URL(query);
			
			URLConnection connection = url.openConnection();
			connection.setConnectTimeout(7000);
			connection.setReadTimeout(7000);
 
			
			BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
			
			//BufferedReader -> String
			String strXml = in.readLine();
			String tmpLine;
			while ((tmpLine = in.readLine()) != null) 
				strXml = strXml + "\n" + tmpLine;
			

			//Create Doc object for the file
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(strXml)));

            //Create xPath object
            XPath xpath = XPathFactory.newInstance().newXPath();

            String results = xpath.evaluate("/GeocodeResponse/status", document);
 
            if(results.equals("OK"))
            	return true;

		}
		catch(Exception e)
		{
			return false;
		}
		
		return false;
	}
	
	
	//Getters:
	public String getTotalDistance() {return totalDistance;}//return distance
	public String getTotalDuration() {return totalDuration;}//return duration
	public int getTotalDurationNumeric(){return totalDurationNumeric;}//return numeric value of duration
	
	public double getSrcLat(){return srcLat;}//return start-location's latitude
	public double getSrcLng(){return srcLong;}//return start-location's longtitude
	
	public double getDestLat(){return destLat;}//return end-location's latitude
	public double getDestLng(){return destLong;}//return end-location's longtitude
	public ArrayList<LatLng> getCoordinates(){return alRoute;}
	public double[] getCoordinatesDoubleArray(){return coordsDoubleArray;}
	
}//EOF
